﻿-- Author  : Kurapica
-- Create Date : 2009/10/12

--[[
	In-Game Addon System
	Studio Part
	FileView
--]]
do
	IGAS:NewAddon("IGAS_Studio")

	-- FileView
	local fileView = CreateFrame("Frame", "FileView", coreF)

	------------------------------------------------
	-- Layout
	------------------------------------------------
	fileView:SetBackdrop{
		bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
		edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
		tile = true, tileSize = 16, edgeSize = 16,
		insets = { left = 3, right = 3, top = 3, bottom = 3 }
	}
	fileView:SetBackdropColor(0,0,0,1)
	fileView.Resizable = true

	local fileTree = CreateFrame("TreeView", "FileList", fileView)
	fileTree:SetPoint("TOPLEFT", fileView, "TOPLEFT", 4, -36)
	fileTree:SetPoint("BOTTOMRIGHT", fileView, "BOTTOMRIGHT", -36, 4)

    local MenuAddon = CreateFrame("DropDownList", "MenuAddon", fileView)
	local MenuScript = CreateFrame("DropDownList", "MenuScript", fileView)
	local MenuLocal = CreateFrame("DropDownList", "MenuLocal", fileView)
	local MenuFrame = CreateFrame("DropDownList", "MenuFrame", fileView)
	------------------------------------------------
	-- Scripts
	------------------------------------------------
	-- DblClk handlers
	local function OnDoubleClick_Addon(self)
		return self:FireEvent("IGAS_STUDIO_ADDONINFO_REQUIRE", self._Value)
	end

	local function OnDoubleClick_File(self)
		return self:FireEvent("IGAS_STUDIO_FILEOPEN_REQUIRE", self._Addon, self._Value, "Content")
	end

	local function OnDoubleClick_Frame(self)
		return self:FireEvent("IGAS_STUDIO_DESIGNEROPEN_REQUIRE", self._Addon, self._Value)
	end

	--- GatherLocal
	local function GatherLocal(content, L)
		for str in gmatch(content, "[^_%w]L(%b[])") do
			L[strmatch(str, "%[(.*)%]")] = true
		end
	end

	local function GatherFile(file, L)
		if file.Type == "Frame" then
			GatherLocal(file.Designer, L)
			GatherLocal(file.Script, L)
		elseif file.Content then
			GatherLocal(file.Content, L)
		end
	end

	local function GatherFolder(folder, L)
		for _, file in ipairs(folder) do
			if type(file) == "table" then
				if file.Type == "Folder" then
					GatherFolder(file, L)
				else
					GatherFile(file, L)
				end
			end
		end
	end

	local function GatherAddon(addon)
		local L = {}

		for _, f in ipairs(addon) do
			if type(f) == "table" then
				if f.Type == "Folder" then
					GatherFolder(f, L)
				else
					GatherFile(f, L)
				end
			end
		end

		return L
	end

	--- LoadAddon
	local function LoadFile(base, file, addon, noMenu)
		local menu = MenuScript
		local OnDblClk = OnDoubleClick_File

		local node = base:GetNodeByValue(file.Name) or base:AddNode{["Text"] = file.Name, ["Value"] = file.Name,}
		node._Value = file
		node._Addon = addon

		if file.Type == "Localization" then
			menu = MenuLocal
		elseif file.Type == "Widget" then
			menu = MenuScript
		elseif file.Type == "Script" then
			menu = MenuScript
		elseif file.Type == "Frame" then
			menu = MenuFrame
			OnDblClk = OnDoubleClick_Frame
		end

		if not noMenu then
			node.Menu = menu
		end
		node.OnDoubleClick = OnDblClk
	end

	local function LoadFolder(base, folder, addon)
		local node = base:GetNodeByValue(folder.Name) or base:AddNode{["Text"] = folder.Name, ["Value"] = folder.Name,}
		node._Value = folder
		node._Addon = addon

		for _, file in ipairs(folder) do
			if type(file) == "table" then
				if file.Type == "Folder" then
					LoadFolder(node, file, addon)
				else
					LoadFile(node, file, addon)
				end
			end
		end
	end

	local function LoadAddon(name)
		local addon = IGAS_STUDIO_DB.FileDB[name]
		local node

		if not addon then return end

		node = fileTree:GetNodeByValue(name) or fileTree:AddNode{["Text"] = name, ["Value"] = name,}
		node._Value = addon
		node.Menu = MenuAddon
		node.OnDoubleClick = OnDoubleClick_Addon

		for _, f in ipairs(addon) do
			if type(f) == "table" then
				if f.Type == "Folder" then
					LoadFolder(node, f, addon)
				else
					LoadFile(node, f, addon, true)
				end
			end
		end
	end

	-- Run&Stop Addon
	local _RunningAddon = {}

	local function LoadCode(name, code)
		local func, err = loadstring(code)
		
		if not func then
			error("["..name.."]"..err)
		else
			func()
		end
	end
	
	local function RunFile(file)
		Log(1, "[FileView][RunFile]"..file.Name)

		if file.Type == "Frame" then
			LoadCode(file.Name..".Designer", file.Designer)
			LoadCode(file.Name..".Script", file.Script)
		elseif file.Content then
			LoadCode(file.Name, file.Content)
		end
		Log(1, "[FileView][RunFile]"..file.Name.." is Okay!!!")
	end

	local function RunFolder(folder)
		Log(1, "[FileView][RunFolder]"..folder.Name)

		for _, file in ipairs(folder) do
			if type(file) == "table" then
				if file.Type == "Folder" then
					RunFolder(file)
				else
					RunFile(file)
				end
			end
		end
	end

	local function RunAddon(name)
		local addon = IGAS_STUDIO_DB.FileDB[name]

		Log(1, "[FileView][RunAddon]"..name)
		for _, f in ipairs(addon) do
			if type(f) == "table" then
				if f.Type == "Folder" then
					RunFolder(f)
				else
					RunFile(f)
				end
			end
		end
	end

	local function StopAddon(name)
		if _RunningAddon[name] then
			_RunningAddon[name]:Dispose()
			_RunningAddon[name] = nil
		elseif type(name) == "table" and name.Dispose then
			name:Dispose()
		end
	end

	-- Replace Addon
	local function replaceFile(file, old, new)
		for i, v in pairs(file) do
			if i ~= "Type" and type(v) == "string" then
				file[i] = gsub(v, old, new)
			end
		end
	end

	local function replaceFolder(folder, old, new)
		for i, v in ipairs(folder) do
			if v.Type == "Folder" then
				replaceFolder(v, old, new)
			else
				replaceFile(v, old, new)
			end
		end
	end

	local function renameAddon(old, new)
		if old == new then
			return false
		end
		if IGAS_STUDIO_DB.FileDB[new] or IGAS:IsAddonExist(new) then
			IGAS:MsgBox("There is an addon with that name.")
			return false
		end

		IGAS_STUDIO_DB.FileDB[new] = IGAS_STUDIO_DB.FileDB[old]
		IGAS_STUDIO_DB.FileDB[old] = nil

		for i, v in pairs(IGAS_STUDIO_DB.FileDB[new]) do
			if v.Type == "Folder" then
				replaceFolder(v, old, new)
			else
				replaceFile(v, old, new)
			end
		end

		return true
	end

	local function renameFile(node, new)
		local file = node._Value

		if file.Name == new then
			return false
		end

		for _, v in ipairs(node.Parent._Value) do
			if v ~= file and v.Name == new then
				IGAS:MsgBox("There is a file with that name.")
				return false
			end
		end

		replaceFile(file, file.Name, new)

		file.Name = new

		node:SetNode{["Text"] = file.Name, ["Value"] = file.Name,}

		node:FireEvent("IGAS_STUDIO_FILECLOSE_REQUIRE", file)

		return true
	end

	local function swapFile(folderNode, from, to)
		folderNode._Value[from], folderNode._Value[to] = folderNode._Value[to], folderNode._Value[from]
		folderNode:GetNode(from):SetNode{["Text"] = folderNode._Value[from].Name, ["Value"] = folderNode._Value[from].Name,}
		folderNode:GetNode(from)._Value = folderNode._Value[from]
		folderNode:GetNode(to):SetNode{["Text"] = folderNode._Value[to].Name, ["Value"] = folderNode._Value[to].Name,}
		folderNode:GetNode(to)._Value = folderNode._Value[to]
	end

	------------------------------------------------
	-- Menus
	------------------------------------------------
	-- Addon's Menu
	---- Run Addon
	local menuRun = MenuAddon:AddMenuButton(L["Run"])

	local _RunAddonName

	local function CallBack_RunAddon(lst)
		if not _RunAddonName or not lst then return end
		
		local status, err = pcall(RunAddon, _RunAddonName)

		if not status then
			errorhandler(strmatch(err, "%[.*$"))
			pcall(StopAddon, IGAS:GetAddon(_RunAddonName))
		else
			local addon = IGAS:GetAddon(_RunAddonName)
			_RunningAddon[_RunAddonName] = addon
			
			for _, event in ipairs(lst) do
				addon:Fire(event, _RunAddonName)
			end
		end
		
		_RunAddonName = nil
	end

	function menuRun:OnClick()
		if MenuAddon.Parent then
			_RunAddonName = MenuAddon.Parent.Text
			return GetLoadEventList(CallBack_RunAddon)
		end
	end
	---- Stop Addon
	local menuStop = MenuAddon:AddMenuButton(L["Stop"])
	function menuStop:OnClick()
		if MenuAddon.Parent then
			local status, err = pcall(StopAddon, MenuAddon.Parent.Text)

			if not status then
				errorhandler(err)
			else
				_RunningAddon[MenuAddon.Parent.Text] = nil
			end
		end
	end
	---- Register Event
	local mnuReg = MenuAddon:AddMenuButton(L["RegisterEvent"])
	function mnuReg:OnClick()
		if MenuAddon.Parent then
			self:FireEvent("IGAS_STUDIO_REGISTER_EVENT", MenuAddon.Parent._Value, true)
		end
	end
	---- Add File
    local mnuAdd = MenuAddon:AddMenuButton(L["Add File"])
	function mnuAdd:OnClick()
		if MenuAddon.Parent then
			self:FireEvent("IGAS_STUDIO_NEWFILE_REQUIRE", MenuAddon.Parent.Text)
		end
	end
	---- Rename Addon
	local mnuRename = MenuAddon:AddMenuButton(L["Rename Addon"])
	function mnuRename:OnClick()
		local node = MenuAddon.Parent
		if not node then return end
		IGAS:MsgBox("Input the new name of this addon.", "ic", function(name)
			if name and renameAddon(node.Text, name) then
				node:Dispose()
				LoadAddon(name)
			end
		end)
	end
	---- Delete Addon
	local mnuDelete = MenuAddon:AddMenuButton(L["Delete Addon"])
	function mnuDelete:OnClick()
		local node = MenuAddon.Parent
		if not node then return end
		IGAS:MsgBox("Are you sure to delete this addon?", "c", function(value)
			if value then
				IGAS_STUDIO_DB.FileDB[node.Text] = nil
				node:Dispose()
			end
		end)
	end
	---- Export Addon
	local menuExport = MenuAddon:AddMenuButton(L["Export Addon"])
	function menuExport:OnClick()
		if MenuAddon.Parent then
			self:FireEvent("IGAS_STUDIO_EXPORT_ADDON", MenuAddon.Parent.Text)
		end
	end
	---- Script for MenuAddon
	function MenuAddon:OnShow()
		if _RunningAddon[self.Parent.Text] then
			menuRun:Disable()
			menuStop:Enable()
		else
			menuRun:Enable()
			menuStop:Disable()
		end
	end

	-- Scripts' Menu
	---- Rename File
    local mnuSRename = MenuScript:AddMenuButton(L["Rename File"])
	function mnuSRename:OnClick()
		local node = MenuScript.Parent
		IGAS:MsgBox("Input the new name of this addon.", "ic", function(name)
			if name then
				renameFile(node, name)
			end
		end)
	end
	---- Load Earlier
	local menSLoadEarlier = MenuScript:AddMenuButton(L["Load Earlier"])
	function menSLoadEarlier:OnClick()
		local node = MenuScript.Parent
		if node.ID > 1 then
			swapFile(node.Parent, node.ID, node.ID - 1)
		end
	end
	---- Load Later
	local menSLoadLater = MenuScript:AddMenuButton(L["Load Later"])
	function menSLoadLater:OnClick()
		local node = MenuScript.Parent
		if node.ID < node.Parent.ChildNodeNum then
			swapFile(node.Parent, node.ID, node.ID + 1)
		end
	end
	---- Delete File
	local menSDel = MenuScript:AddMenuButton(L["Delete File"])
	function menSDel:OnClick()
		local node = MenuScript.Parent
		IGAS:MsgBox("Are you sure to delete this file?", "c", function(value)
			if value then
				tremove(node.Parent._Value, node.ID)
				node:Dispose()
			end
		end)
	end

	-- Local' Menu
	---- Update Local
	local menLUpdate = MenuLocal:AddMenuButton(L["Update All"])
	function menLUpdate:OnClick()
		local L = GatherAddon(MenuLocal.Parent._Addon)

		for _, file in ipairs(MenuLocal.Parent.Parent._Value) do
			if file.Type == "Localization" then
				-- Add not exist
				for str in pairs(L) do
					if not strfind(file.Content, str) then
						if strfind(file.Content, "\n$") then
							file.Content = file.Content.."L["..str.."] = "..str.."\n"
						else
							file.Content = file.Content.."\n".."L["..str.."] = "..str.."\n"
						end
					end
				end

				-- remove not exist
				for _, str in gmatch(file.Content, "L(%b[])%s*=%s*[^\n]*") do
					if str then
						str = strmatch(str, "%[(.*)%]")
						if not L[str] then
							file.Content = gsub(file.Content, "L%["..str.."%]%s*=%s*[^\n]*\n", "")
							file.Content = gsub(file.Content, "L%["..str.."%]%s*=%s*[^\n]*", "")
						end
					end
				end

				self:FireEvent("IGAS_STUDIO_FILERELOAD_REQUIRE", file)
			end
		end
	end
	---- Load Earlier
	local menLLoadEarlier = MenuLocal:AddMenuButton(L["Load Earlier"])
	function menLLoadEarlier:OnClick()
		local node = MenuLocal.Parent
		if node.ID > 1 then
			swapFile(node.Parent, node.ID, node.ID - 1)
		end
	end
	---- Load Later
	local menLLoadLater = MenuLocal:AddMenuButton(L["Load Later"])
	function menLLoadLater:OnClick()
		local node = MenuLocal.Parent
		if node.ID < node.Parent.ChildNodeNum then
			swapFile(node.Parent, node.ID, node.ID + 1)
		end
	end
	---- Delete Local
	local menLDel = MenuLocal:AddMenuButton(L["Delete File"])
	function menLDel:OnClick()
		local node = MenuLocal.Parent
		IGAS:MsgBox("Are you sure to delete this file?", "c", function(value)
			if value then
				tremove(node.Parent._Value, node.ID)
				node:Dispose()
			end
		end)
	end

	-- Frame' Menu
	-- Show Script
	local mnuFScript = MenuFrame:AddMenuButton(L["Show Script"])
	function mnuFScript:OnClick()
		local node = MenuFrame.Parent
		return node:FireEvent("IGAS_STUDIO_FILEOPEN_REQUIRE", node._Addon, node._Value, "Script")
	end
	-- Show Designer
	local mnuFDesigner = MenuFrame:AddMenuButton(L["Show Designer"])
	function mnuFDesigner:OnClick()
		local node = MenuFrame.Parent
		return node:FireEvent("IGAS_STUDIO_FILEOPEN_REQUIRE", node._Addon, node._Value, "Designer")
	end
	---- Rename File
    local mnuFRename = MenuFrame:AddMenuButton(L["Rename File"])
	function mnuFRename:OnClick()
		local node = MenuFrame.Parent
		IGAS:MsgBox("Input the new name of this addon.", "ic", function(name)
			if name then
				renameFile(node, name)
			end
		end)
	end
	---- Load Earlier
	local menFLoadEarlier = MenuFrame:AddMenuButton(L["Load Earlier"])
	function menFLoadEarlier:OnClick()
		local node = MenuFrame.Parent
		if node.ID > 1 then
			swapFile(node.Parent, node.ID, node.ID - 1)
		end
	end
	---- Load Later
	local menFLoadLater = MenuFrame:AddMenuButton(L["Load Later"])
	function menFLoadLater:OnClick()
		local node = MenuFrame.Parent
		if node.ID < node.Parent.ChildNodeNum then
			swapFile(node.Parent, node.ID, node.ID + 1)
		end
	end
	---- Delete File
	local menFDel = MenuFrame:AddMenuButton(L["Delete File"])
	function menFDel:OnClick()
		local node = MenuFrame.Parent
		IGAS:MsgBox("Are you sure to delete this file?", "c", function(value)
			if value then
				tremove(node.Parent._Value, node.ID)
				node:Dispose()
			end
		end)
	end

	------------------------------------------------
	-- Custom Events
	------------------------------------------------
	fileView:RegisterEvent("VARIABLES_LOADED")
	fileView:RegisterEvent("IGAS_STUDIO_ADDONINFO_SAVE")
	fileView:RegisterEvent("IGAS_STUDIO_NEWFILE_SAVE")

	function fileView:VARIABLES_LOADED()
		IGAS_STUDIO_DB.FileDB = IGAS_STUDIO_DB.FileDB or {}

		for addonName in pairs(IGAS_STUDIO_DB.FileDB) do
			LoadAddon(addonName)
		end
	end

	function fileView:IGAS_STUDIO_ADDONINFO_SAVE(addonName)
		LoadAddon(addonName)
	end

	function fileView:IGAS_STUDIO_NEWFILE_SAVE(addonName, fileType, fileName)
		LoadAddon(addonName)
	end
end
